//
// @Author: zhengqi Wu
// @Description: 实现通用的MIN推式包
// @Version: 1.0.0
// @Date: 2021/3/3 16:23
// @Copyright: MIN-Group；国家重大科技基础设施——未来网络北大实验室；深圳市信息论与未来网络重点实验室
// DONE： 联调四个分区
//


const encoding = require('../encoding')
const TLV = require("../encoding/TLV"); // 引入encoding模块
const {IdentifierField} = require('../component/IdentifierField')
const {SignatureField} = require('../component/SignatureField')
const {ReadOnlyField} = require('../component/ReadOnlyField')
const {MutableField} = require('../component/MutableField')
//
// 表示一个MIN网络层包的通用结构
//
// @Description:
//	1. 格式如下：
//		MINPacket = PACKET-TYPE TLV-LENGTH
//             { CommonIdentifier }          => 标识区
//             { Signature }                 => 签名区
//             {                             => 只读区
//                 <Payload>
//             }
//             {                             => 可变区
//                 {                         => 受保护区
//                     "Empty"
//                 }
//                 {                         => 非受保护区
//                     [TTL]
//                 }
//             }
//  2. 实现encoding.IEncodingAble接口
class MINPacket extends encoding.IEncodingAble{
    constructor() {
        super(); // 调用IEncodingAble的构造函数：检查MINPacket类是否实现 wireEncode 与 wireDecode 两个函数
        // Go中的匿名类型 => js中的成员变量
        // 本质：拥有该类型对应的能力
        this.IdentifierField = new IdentifierField() // 标识区
        this.SignatureField = new SignatureField() // 签名区
        this.ReadOnlyField = new ReadOnlyField() // 只读区
        this.MutableField = new MutableField() // 可变区
        this.packetType = (0) // 包类型 encoding.tlv.Vlint
    }

    /**
     * 判断 MIN 包是否是一个有效的包
     * @returns {boolean}
     */
    isPacketValid(){
        return TLV.isValidPacketType(this.packetType)
    }

    /**
     * 将 Packet 线速编码为 TLV
     * @param enc encoding.Encoder
     * @returns {number} TLV总长度
     */
    wireEncode(enc){
        if(!this.isPacketValid()){
            // DONE 错误处理
            console.log("packetType", this.packetType)
            throw new MINPacketError()
        }
        let totalLength = 0

        // 编码TLV-VALUE
        try {
            // 可变区
            let tmpLen = this.MutableField.wireEncode(enc)
            totalLength += tmpLen

            // 只读区
            tmpLen = this.ReadOnlyField.wireEncode(enc)
            totalLength += tmpLen

            // 签名区
            tmpLen = this.SignatureField.wireEncode(enc)
            totalLength += tmpLen

            // 标识区
            tmpLen = this.IdentifierField.wireEncode(enc)
            totalLength += tmpLen

            // 编码TLV-LENGTH:totalLength Number
            tmpLen = enc.prependVarNumber((totalLength))
            totalLength += tmpLen

            // 编码TLV-TYPE:packetType Vlint
            tmpLen = enc.prependVarNumber(this.packetType)
            totalLength += tmpLen

            return totalLength
        } catch(e){
            throw e
        }
        
    }

    /**
     * @Description:
     *  1. 解析出四个Field的TLV
     *  2. 检查必要的TLV是否存在，不存在则抛出异常，以下为必须条件
     *      a. 必须包含标识区，且标识区至少包含一个标识
     * @param block encoding.block.Block
     */
    wireDecode(block){
        if(!block.isValid()){
            // DONE 错误处理
            throw new MINPacketError()
        }
        this.packetType = block.getType() // Add 解析MINPacket类型
        block.parseSubElements() // 解析子Block
        // 标记，用来记录是否满足必要条件
        let existIdentifier = false

        // 解析四个标识区
        let subElements = block.getSubElements()
        try {
            for (let i = 0; i < subElements.length; i++) {
                let subElement = subElements[i]
                switch (subElement.getType()) {
                    case encoding.tlv.FieldType.TlvIdentifierField:
                        // 解析标识区，保证其中至少存在一个标识
                        subElement.parseSubElements()
                        if (!subElement.hasSubElement()) {
                            // DONE 错误处理
                            throw new MINPacketError()
                        }
                        // 解码标识区 出错会抛出错误
                        this.IdentifierField.wireDecode(subElement)
                        // 标示解析到了至少一个以上的合法标识
                        existIdentifier = true
                        break
                    case encoding.tlv.FieldType.TlvSignatureField:
                        // 解析签名区，保证如果存在签名，则签名均符合格式，出错会抛出错误
                        this.SignatureField.wireDecode(subElement)
                        break
                    case encoding.tlv.FieldType.TlvReadOnlyField:
                        // 解析只读区
                        this.ReadOnlyField.wireDecode(subElement)
                        break
                    case encoding.tlv.FieldType.TlvMutableField:
                        this.MutableField.wireDecode(subElement)
                        break
                    default:
                        throw new MINPacketError("Invalid TLV")
                    // 无效TLV TODO 抛出错误
                }
            }
        } catch (e) {
            throw e
        }
        

        // 判断是否有必须的组件缺失
        if(!existIdentifier){
            // DONE 抛出错误
            throw new MINPacketError()
        }
    }

    GetPacketType()  {
        var identifierWrapper = this.IdentifierField.getIdentifier(0)
        return [identifierWrapper.getIdentifierType(), null]
    }
}

class MINPacketError extends Error {
    constructor(m) {
        super(m)
        this.name = 'MINPacketError'
    }

    error() {
        console.log(`${this.name}: ${this.message}`)
    }
}

module.exports = MINPacket